﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SourceSafeTypeLib;
using CustomControls.TaskDialog;
using Microsoft.Win32;

namespace SubVisual.GUI
{
    public partial class MainForm : Form
    {
        private VSSDatabase vssDb;
        private TreeNode topNode;
        private Migrator migrator;

        public MainForm()
        {
            InitializeComponent();
            toolStripProgressBar.Enabled = false;
            topNode = vssProjectsTreeview.Nodes[0];
            RegistryKey svnHistoryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\TortoiseSVN\History\repoURLS");
            if (svnHistoryKey != null)
            {
                String[] svnHistoryValues = svnHistoryKey.GetValueNames();
                foreach (String valueName in svnHistoryValues)
                {
                    svnUrlTextBox.Items.Add(svnHistoryKey.GetValue(valueName));
                }
            }
        }

        private void openSrcSafeIniButton_Click(object sender, EventArgs e)
        {
            if (openSrcSafeIniDialog.ShowDialog() == DialogResult.OK)
            {
                vssDb = new VSSDatabaseClass();
                // Try initial login with blank credentials; are they cached?
                if (DoVssLogin(openSrcSafeIniDialog.FileName, null, ""))
                {
                    vssIniLocation.Text = openSrcSafeIniDialog.FileName;
                    Properties.Settings.Default["DefaultVssUser"] = vssDb.Username;
                    Properties.Settings.Default.Save();
                    GetVssProjects(this, EventArgs.Empty);
                }
            }
        }

        private bool DoVssLogin(String vssIniFile, String userName, String password)
        {
            try
            {
                vssDb.Open(vssIniFile, userName ?? "", password);
                return true;
            }
            catch (System.Runtime.InteropServices.COMException e)
            {
                if (e.ErrorCode == -2147166526) // Invalid user name
                {
                    if (!String.IsNullOrEmpty(userName))
                    {
                        TaskDialog td = new TaskDialog();
                        td.WindowTitle = Application.ProductName;
                        td.MainIcon = TaskDialogIcon.Error;
                        td.MainInstruction = "Invalid username";
                        td.Content = "You have supplied an invalid username, please review credentials.";
                        td.CommonButtons = TaskDialogCommonButtons.Retry | TaskDialogCommonButtons.Cancel;
                        if (td.Show(this) == (int)TaskDialogCommonButtons.Cancel)
                            return false;
                    }
                    GUI.VssCredentialsUI vssCred = new VssCredentialsUI(this);
                    SharpSvn.Security.SvnUserNamePasswordEventArgs credArgs =
                        new SharpSvn.Security.SvnUserNamePasswordEventArgs(userName ?? Properties.Settings.Default["DefaultVssUser"] as String, openSrcSafeIniDialog.FileName, true);
                    vssCred.DialogUserNamePasswordHandler(this, credArgs);
                    if (!credArgs.Cancel && !credArgs.Break)
                    {
                        return DoVssLogin(vssIniLocation.Text, credArgs.UserName, credArgs.Password);
                    }
                    else
                        return false;
                }
                else
                {
                    // TODO: Extend error handling for VSS login
                    TaskDialog td = new TaskDialog();
                    td.WindowTitle = Application.ProductName;
                    td.MainIcon = TaskDialogIcon.Error;
                    td.MainInstruction = "An unexpected error has occurred";
                    td.Content = "Please report the following trace to " + Application.ProductName + " contributors:\n\n";
                    td.Content += e.Message + "\n\n" + e.StackTrace;
                    td.CommonButtons = TaskDialogCommonButtons.Ok;
                    td.Show(this);
                    return false;
                }
            }
            catch (Exception e)
            {
                // TODO: Extend error handling for VSS login
                TaskDialog td = new TaskDialog();
                td.WindowTitle = Application.ProductName;
                td.MainIcon = TaskDialogIcon.Error;
                td.MainInstruction = "An unexpected error has occurred";
                td.Content = "Please report the following trace to " + Application.ProductName + " contributors:\n\n";
                td.Content += e.Message + "\n\n" + e.StackTrace;
                td.CommonButtons = TaskDialogCommonButtons.Ok;
                td.Show(this);
                return false;
            }
        }

        private void browseForSvnDbLocationButton_Click(object sender, EventArgs e)
        {
            if (svnDbFolderBrowserDialog.ShowDialog() == DialogResult.OK)
                if(!svnDbFolderBrowserDialog.SelectedPath.ToLower().EndsWith(@"\db\revprops"))
                {
                    TaskDialog td = new TaskDialog();
                    td.CommonButtons = TaskDialogCommonButtons.Yes | TaskDialogCommonButtons.No;
                    td.WindowTitle=Application.ProductName;
                    td.MainIcon= TaskDialogIcon.Warning;
                    td.MainInstruction = "Do you really wish to use this path?";
                    td.Content="The path to the revisions database should end with '\\db\\revprops' which yours does not.\n\nPlease note that if this path is not correct, migrateted revisions will not keep original author and timestamp.";
                    if(td.Show(this)==(int) DialogResult.No)
                    {
                        browseForSvnDbLocationButton_Click(sender, e);
                        return;
                    }
                }
                svnDbLocation.Text = svnDbFolderBrowserDialog.SelectedPath;
        }

        private void GetVssProjects(object sender, EventArgs e)
        {
            toolStripProgressBar.Enabled = true;
            toolStripProgressBar.Style = ProgressBarStyle.Marquee;
            this.Cursor = Cursors.WaitCursor;
            this.toolStripStatusLabel.Text = "Opening database...";

            this.topNode.Nodes.Clear();
            TreeNode dbNode = vssProjectsTreeview.Nodes[0].Nodes.Add(vssDb.SrcSafeIni);
            dbNode.Tag = vssDb;
            dbNode.ImageIndex = 3;
            dbNode.SelectedImageIndex = 3;

            VSSItem vssRootItem = vssDb.get_VSSItem(@"$\", false);
            TreeNode dbTopNode = AddProjectNode(dbNode, vssRootItem, false);

            ExpandProjectNode(dbTopNode);

            topNode.Collapse(false);
            dbTopNode.EnsureVisible();
            dbTopNode.Expand();

            this.toolStripProgressBar.Enabled = false;
            this.toolStripProgressBar.Style = ProgressBarStyle.Continuous;
            this.toolStripProgressBar.Value = 0;

            this.Cursor = Cursors.Default;
            this.toolStripStatusLabel.Text = "Ready";
        }

        private TreeNode AddProjectNode(TreeNode parentTreeNode, IVSSItem vssItem, bool traverse)
        {
            TreeNode tn = parentTreeNode.Nodes.Add(!String.IsNullOrEmpty(vssItem.Name) ? vssItem.Name : vssItem.Spec);
            tn.Tag = vssItem;
            tn.Checked = parentTreeNode.Checked;

            if (vssItem.get_Items(false).Count != 0)
            {
                int projects = 0, files = 0;
                foreach (IVSSItem item in vssItem.get_Items(false))
                {
                    if (item.Type == (int)VSSItemType.VSSITEM_PROJECT)
                    {
                        projects++;
                        if (traverse)
                            AddProjectNode(tn, item, true);
                    }
                    else { files++; }
                }
                tn.ToolTipText =
                    "Project contains:\r\n" +
                    projects.ToString() + " projects and \r\n" +
                    files.ToString() + " files";
                if(projects>0)
                    tn.Nodes.Add("Loading...");
            }
            else { tn.ToolTipText = "Project is empty"; }
            return tn;
        }

        private void ExpandProjectNode(TreeNode treeNode)
        {
            treeNode.Nodes.Clear();
            foreach (IVSSItem item in (treeNode.Tag as VSSItem).get_Items(false))
            {
                if (item.Type == (int)VSSItemType.VSSITEM_PROJECT)
                    AddProjectNode(treeNode, item, false);
            }
        }

        private void vssProjectsTreeview_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Node.Checked)
                migrateButton.Enabled = true;
            foreach (TreeNode tn in e.Node.Nodes)
            {
                TraverseTree(tn, true, e.Node.Checked);
            }
        }

        private void TraverseTree(TreeNode tn, bool downwards, bool setChecked)
        {
            tn.Checked = setChecked;
            if (downwards)
                foreach (TreeNode stn in tn.Nodes)
                    TraverseTree(stn, true, setChecked);
            else
                TraverseTree(tn.Parent, false, setChecked);
        }

        private void vssProjectsTreeview_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            if (e.Node.Level > 1 && e.Node.Tag != null && e.Node.Tag is VSSItem && e.Node.Nodes.Count == 1 && e.Node.Nodes[0].Text == "Loading...")
            {
                toolStripProgressBar.Style = ProgressBarStyle.Marquee;
                toolStripProgressBar.Enabled = true;
                this.Cursor = Cursors.WaitCursor;
                this.toolStripStatusLabel.Text = "Reading projects...";
                
                ExpandProjectNode(e.Node);

                toolStripProgressBar.Style = ProgressBarStyle.Continuous;
                toolStripProgressBar.Enabled = false;
                this.Cursor = Cursors.Default;
                this.toolStripStatusLabel.Text = "Ready";
            }
        }

        private void migrateButton_Click(object sender, EventArgs e)
        {
            if (migrateButton.Text == "Cancel")
            {
                TaskDialog td = new TaskDialog();
                td.WindowTitle = "Cancel migration";
                td.EnableHyperlinks = true;
                td.MainInstruction = "Do you wish to cancel migration?";
                td.Content = "Please note that cancelling might leave the <a href=\"" + svnUrlTextBox.Text + "\">SVN repository</a> in an unknown state.";
                List<TaskDialogButton> buttons = new List<TaskDialogButton>();
                buttons.Add(new TaskDialogButton(100, "No , continue migration"));
                buttons.Add(new TaskDialogButton(101, "Yes, cancel migration"));
                td.Buttons = buttons.ToArray();
                td.DefaultButton = 100;
                td.UseCommandLinks = true;

                if (td.Show(this) == 101)
                {
                    migrationBackgroundWorker.CancelAsync();
                    migrator.CancelExecution();
                }
            }
            else
            {
                // Add current SVN repos to list and create Migrator object
                
                List<object> arguments = new List<object>(2);

                migrator = new Migrator(vssDb, svnUrlTextBox.Text, svnDbLocation.Text, this);
                migrator.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(m_ProgressChanged);
                migrator.AllowExistingDirectories = allowDirExistsCheckBox.Checked;

                List<IVSSItem> projects = new List<IVSSItem>();
                GetSelectedProjects(topNode, projects);
                toolStripProgressBar.Style = ProgressBarStyle.Continuous;
                toolStripProgressBar.Value = 0;
                toolStripProgressBar.Enabled = true;

                arguments.Add(migrator);
                arguments.Add(projects);

                vssGroupBox.Enabled = false;
                svnGroupBox.Enabled = false;
                vssProjectsTreeview.Enabled = false;
                migrateButton.Text = "Cancel";
                migrateButton.ImageIndex = 4;

                migrationBackgroundWorker.RunWorkerAsync(arguments);
            }
        }

        private void GetSelectedProjects(TreeNode tn, List<IVSSItem> projects)
        {
            if(tn.Checked && tn.Tag != null)
                projects.Add(tn.Tag as IVSSItem);
            else
                foreach(TreeNode subnode in tn.Nodes)
                {
                    GetSelectedProjects(subnode, projects);
                }
            return;
        }


        void m_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            migrationBackgroundWorker.ReportProgress(e.ProgressPercentage, e.UserState as String);
        }

        private void migrationBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            List<object> arguments = e.Argument as List<object>;
            Migrator m = arguments[0] as Migrator;
            List<IVSSItem> projects = arguments[1] as List<IVSSItem>;
            if (m.Execute(projects))
                e.Result = new List<String>() {m.LastResult, m.LastResultDetails};
            else
                if (m.Cancelled)
                {
                    e.Cancel = true;
                    e.Result = "Migration was cancelled by user";
                }
                else
                    throw new ApplicationException(m.ErrorMessage);
        }

        private void migrationBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.toolStripProgressBar.Value = e.ProgressPercentage;
            this.toolStripProgressBar.ToolTipText = String.Format("{0}% completed", e.ProgressPercentage);
            this.toolStripStatusLabel.Text = e.UserState as String;
        }

        private void migrationBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
                TaskDialogs.ShowDialog(Application.ProductName, "An error was encountered during migration", e.Error.Message, e.Error.StackTrace, CustomControls.TaskDialog.TaskDialogIcon.Error, "Please check settings and try again", CustomControls.TaskDialog.TaskDialogIcon.Information, false);
            else if (e.Cancelled)
                TaskDialogs.ShowDialog(Application.ProductName, "Action was cancelled by user", "State of SVN repository is unknown, cleanup may be required", CustomControls.TaskDialog.TaskDialogIcon.Information, false);
            else
            {
                String result = (e.Result as List<String>).ElementAt<String>(0);
                string details = (e.Result as List<String>).ElementAt<String>(1);
                TaskDialogs.ShowDialog(Application.ProductName, result, null, details, CustomControls.TaskDialog.TaskDialogIcon.Information, null, TaskDialogIcon.None, false);
            }

            this.vssGroupBox.Enabled = true;
            this.svnGroupBox.Enabled = true;
            this.vssProjectsTreeview.Enabled = true;
            this.migrateButton.Text = "Migrate";
            this.migrateButton.ImageIndex = 5;

            this.toolStripProgressBar.Value = 0;
            this.toolStripProgressBar.Enabled = false;
            this.toolStripStatusLabel.Text = "Ready";
        }
    }
}
